home *** CD-ROM | disk | FTP | other *** search
/ Hardcore Visual Basic 5.0 (2nd Edition) / Hardcore Visual Basic 5.0 - Second Edition (1997)(Microsoft Press).iso / Code / split.cls < prev    next >
Text File  |  1997-06-14  |  15KB  |  446 lines

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4. END
  5. Attribute VB_Name = "CSplitter"
  6. Attribute VB_GlobalNameSpace = False
  7. Attribute VB_Creatable = True
  8. Attribute VB_PredeclaredId = False
  9. Attribute VB_Exposed = True
  10. Option Explicit
  11.  
  12. ' Thanks to Elliott Whitticar and Vadim Katsman who fixed bugs and suggested
  13. ' improvements to my CHSplitter and CVSplitter classes. I integrated some of
  14. ' their code, but didn't solve all possible splitter problems. The primary
  15. ' improvement (implemented separately by both Elliot and Vadim) is combining
  16. ' vertical and horizontal splitters into a single class. Thanks.
  17.  
  18. ' Internal variables for forms and controls
  19. Private ctlNW As Object             ' Left/Top control
  20. Private ctlSE As Object             ' Right/Bottom control
  21. Private objContainer As Object
  22.  
  23. ' Sizes of borders and pixels
  24. Private xySplit As Single           ' Position of splitter bar
  25. Private dxySplit As Single          ' Width/height of splitter bar in scale units
  26. Private dxyMin As Single            ' Minimum control width/height
  27. Private xPixel As Single
  28. Private yPixel As Single
  29. Private dxBorder As Single
  30. Private dyBorder As Single
  31. Private cBorderPixels As Long
  32.  
  33. ' Flags
  34. Private fResize As Boolean          ' True ==> move left and right control
  35. Private fAutoBorder As Boolean
  36. Private fDragging As Boolean
  37. Private fCreated As Boolean
  38. Private fVertical As Boolean        ' True => Vertical splitter, F => Horizontal
  39. Private iPercent As Integer           ' 1 to 99 gives initial split Percent
  40.  
  41. ' Old mouse pointer, draw style, and draw mode
  42. Private mpOld As Integer
  43. Private dsOld As Integer
  44. Private dmOld As Integer
  45.  
  46. Private mpResize As Integer         ' MousePointer to use when resizing
  47.  
  48. ' AutoRedraw
  49. Private arOld As Boolean
  50.  
  51. Public Enum EErrorSplitter
  52.     eeBaseSplitter = 13690  ' CSplitter
  53.     eeInvalidControl        ' Invalid controls or container
  54.     eeSplitNotCreated       ' Create splitter before using members
  55. End Enum
  56.  
  57. #If fComponent = 0 Then
  58. Private Sub ErrRaise(e As Long)
  59.     Dim sText As String, sSource As String
  60.     If e > 1000 Then
  61.         sSource = App.EXEName & ".CSplitter"
  62.         Select Case e
  63.         Case eeBaseAbout
  64.             BugAssert True
  65.         Case eeInvalidControl
  66.             sText = "Create: Invalid controls or container"
  67.         Case eeSplitNotCreated
  68.             sText = "Create splitter before using members"
  69.         End Select
  70.         Err.Raise COMError(e), sSource, sText
  71.     Else
  72.         ' Raise standard Visual Basic error
  73.         Err.Raise e, sSource
  74.     End If
  75. End Sub
  76. #End If
  77.  
  78. ' Create a splitter window
  79. Sub Create(LeftControl As Object, RightControl As Object, _
  80.            Vertical As Boolean, _
  81.            Optional BorderPixels As Long = 0, _
  82.            Optional AutoBorder As Boolean = True, _
  83.            Optional Resizeable As Boolean = True, _
  84.            Optional Percent As Integer = 50, _
  85.            Optional Cursor As Picture)
  86.  
  87.     fCreated = False
  88.     
  89.     On Error GoTo CreateError
  90.     
  91.     ' Set internal controls
  92.     Set ctlNW = LeftControl
  93.     Set ctlSE = RightControl
  94.     Set objContainer = ctlNW.Container
  95.     BugAssert objContainer Is ctlSE.Container
  96.     
  97.     ' Splitters work--sort of--with ClipControls True, but the splitter line
  98.     ' isn't drawn correctly, so we disallow it in debug version (go ahead
  99.     ' and ignore in release if you really want)
  100.     BugAssert objContainer.ClipControls = False
  101.     
  102.     ' Save resizable and AutoBorder flags
  103.     fAutoBorder = AutoBorder
  104.     fResize = Resizeable
  105.     fVertical = Vertical
  106.     
  107.     ' Handle split percent
  108.     iPercent = Percent
  109.     If iPercent > 99 Then iPercent = 99
  110.     If iPercent < 1 Then iPercent = 1
  111.     
  112.     ' Set splitter size
  113.     cBorderPixels = BorderPixels
  114.     If cBorderPixels = 0 Then
  115.         fAutoBorder = True
  116.         cBorderPixels = 4
  117.     End If
  118.     
  119.     With objContainer
  120.         ' Size of one in pixel in current scale
  121.         xPixel = .ScaleX(1, vbPixels, .ScaleMode)
  122.         yPixel = .ScaleY(1, vbPixels, .ScaleMode)
  123.     
  124.         ' Set cursor
  125.         If Cursor Is Nothing Then
  126.             If fVertical Then
  127.                 Set .MouseIcon = LoadResPicture("VSplit", vbResCursor)
  128.             Else
  129.                 Set .MouseIcon = LoadResPicture("HSplit", vbResCursor)
  130.             End If
  131.         Else
  132.             Set .MouseIcon = Cursor
  133.         End If
  134.         ' Get the .MousePointer value to use when resizing
  135.         If .MouseIcon.Type = vbPicTypeIcon Then
  136.             mpResize = vbCustom
  137.         ElseIf fVertical Then
  138.             mpResize = vbSizeWE
  139.         Else
  140.             mpResize = vbSizeNS
  141.         End If
  142.         
  143.         ' Set border size
  144.         If fAutoBorder Then
  145.             dxBorder = ctlNW.Left
  146.             dyBorder = ctlNW.Top
  147.         Else
  148.             dxBorder = cBorderPixels * xPixel
  149.             dyBorder = cBorderPixels * yPixel
  150.         End If
  151.  
  152.         ' Set the splitter bar and minimum width/height in scale units
  153.         ' (Ideally we'd use control properties for minimum width/height)
  154.         If fVertical Then
  155.             dxySplit = cBorderPixels * xPixel
  156.             dxyMin = 20 * xPixel + 2 * dyBorder
  157.         Else
  158.             dxySplit = cBorderPixels * yPixel
  159.             dxyMin = 20 * yPixel + 2 * dyBorder
  160.         End If
  161.         SplitPercent = Percent
  162.  
  163.     End With
  164.     
  165.     fCreated = True
  166.     Exit Sub
  167. CreateError:
  168.     ErrRaise eeInvalidControl
  169. End Sub
  170.  
  171. Property Get Capture() As Boolean
  172.     If Not fCreated Then ErrRaise eeSplitNotCreated
  173.     
  174.     ' See if the container form or control has captured mouse events
  175.     Capture = (GetCapture = objContainer.hWnd)
  176. End Property
  177.  
  178.  
  179. Property Let Capture(fCapture As Boolean)
  180.     If Not fCreated Then ErrRaise eeSplitNotCreated
  181.     
  182.     ' Capture or release mouse events
  183.     If fCapture Then
  184.         SetCapture objContainer.hWnd
  185.     Else
  186.         ReleaseCapture
  187.     End If
  188. End Property
  189.  
  190. Private Sub Draw(ByVal xyDraw As Single, _
  191.                  Optional xyNew As Single = -1#)
  192. With objContainer
  193.     ' Draw the splitter bar at the specified position
  194.     
  195.     ' The second argument is provided so MouseMove can invert the
  196.     ' old drawing at xyDraw and draw a new splitter bar at xyNew
  197.  
  198.     .DrawStyle = vbInsideSolid
  199.     .DrawMode = vbInvert
  200.     If fVertical Then
  201.         ' Erase old line
  202.         objContainer.Line (xyDraw - xPixel, ctlNW.Top)-(xyDraw + xPixel, yBottom(ctlNW)), , B
  203.         If Not IsMissing(xyNew) Then
  204.             ' Draw new line
  205.             xyDraw = xyNew
  206.             objContainer.Line (xyDraw - xPixel, ctlNW.Top)-(xyDraw + xPixel, yBottom(ctlNW)), , B
  207.         End If
  208.     Else
  209.         ' Erase old line
  210.         objContainer.Line (ctlNW.Left, xyDraw - yPixel)-(xRight(ctlNW), xyDraw + yPixel), , B
  211.         If Not IsMissing(xyNew) Then
  212.             ' Draw new line
  213.             xyDraw = xyNew
  214.             objContainer.Line (ctlNW.Left, xyDraw - yPixel)-(xRight(ctlNW), xyDraw + yPixel), , B
  215.         End If
  216.     End If
  217. End With
  218. End Sub
  219.  
  220. Public Property Get SplitterSize() As Long
  221.     ' Width/Height of splitter bar in scale units
  222.     SplitterSize = dxySplit
  223. End Property
  224.  
  225. Public Property Get SplitPercent() As Long
  226. With objContainer
  227.     If fVertical Then
  228.         SplitPercent = 100 / (.ScaleWidth / (xySplit - (dxySplit / 2)))
  229.     Else
  230.         SplitPercent = 100 / (.ScaleHeight / (xySplit - (dxySplit / 2)))
  231.     End If
  232. End With
  233. End Property
  234.  
  235. Public Property Let SplitPercent(ByVal iPercentCur As Long)
  236. With objContainer
  237.     Dim iMinPercent As Long
  238.     If fVertical Then
  239.         iMinPercent = 100 / (.ScaleWidth / (dxyMin - (dxySplit / 2)))
  240.         If iPercentCur < iMinPercent Then iPercentCur = iMinPercent
  241.         If iPercentCur > 100 - iMinPercent Then iPercentCur = 100 - iMinPercent
  242.         xySplit = ((iPercent / 100) * .ScaleWidth) - (dxySplit / 2)
  243.         ctlNW.Move dxBorder, dyBorder, _
  244.                    xySplit - dxBorder, _
  245.                    .ScaleHeight - (dyBorder * 2)
  246.  
  247.         ctlSE.Move xRight(ctlNW) + dxySplit, dyBorder, _
  248.                    .ScaleWidth - ctlNW.Width - (dxBorder * 2), _
  249.                    ctlNW.Height
  250.     Else
  251.         iMinPercent = 100 / (.ScaleHeight / (dxyMin - (dxySplit / 2)))
  252.         If iPercent < iMinPercent Then iPercent = iMinPercent
  253.         If iPercent > 100 - iMinPercent Then iPercent = 100 - iMinPercent
  254.         dxySplit = cBorderPixels * yPixel
  255.         dxyMin = 20 * yPixel + 2 * dyBorder
  256.         xySplit = ((iPercent / 100) * .ScaleHeight) - (dxySplit / 2)
  257.         ctlNW.Move dxBorder, dyBorder, _
  258.                    .ScaleWidth - (dxBorder * 2), _
  259.                    xySplit - dyBorder
  260.                    
  261.         ctlSE.Move dxBorder, yBottom(ctlNW) + dxySplit, _
  262.                    ctlNW.Width, _
  263.                    .ScaleHeight - ctlNW.Height - (dyBorder * 2)
  264.     End If
  265. End With
  266. End Property
  267.  
  268. Sub Resize()
  269. With objContainer
  270.  
  271.     Dim rScaleFac As Single
  272.     Dim dxyStart As Single, dxyFarEdge As Single
  273.     If fVertical Then
  274.         rScaleFac = .ScaleWidth / (dxBorder + ctlNW.Width + dxySplit + _
  275.                                    ctlSE.Width + dxBorder)
  276.         ' Move everything in border size from the edge
  277.         dxyFarEdge = .ScaleHeight - dyBorder - dyBorder
  278.         ctlNW.Move dxBorder, .ScaleTop + dyBorder, _
  279.                    ctlNW.Width * rScaleFac, dxyFarEdge
  280.     
  281.         dxyStart = xRight(ctlNW) + dxySplit
  282.         ctlSE.Move dxyStart, dyBorder, _
  283.                    .ScaleWidth - dxyStart - dxBorder, dxyFarEdge
  284.     Else    ' Resize Horizontal Splitter
  285.  
  286.         rScaleFac = .ScaleHeight / (dyBorder + ctlNW.Height + dxySplit + _
  287.                                     ctlSE.Height + dyBorder)
  288.  
  289.         ' Move everything in border size from the edge
  290.         dxyFarEdge = .ScaleWidth - dxBorder - dxBorder
  291.         ctlNW.Move dxBorder, .ScaleTop + dyBorder, _
  292.                    dxyFarEdge, ctlNW.Height * rScaleFac
  293.     
  294.         dxyStart = yBottom(ctlNW) + dxySplit
  295.         ctlSE.Move dxBorder, dxyStart, dxyFarEdge, _
  296.                    .ScaleHeight - dxyStart - dyBorder
  297.     End If
  298. End With
  299. End Sub
  300.  
  301. Sub Splitter_MouseMove(Button As Integer, Shift As Integer, _
  302.                        X As Single, Y As Single)
  303. With objContainer
  304.     If Not fCreated Then ErrRaise eeSplitNotCreated
  305.     Dim xyNew As Single, xyMinPos As Single, xyMaxPos As Single
  306.     Dim fZone As Boolean    ' Over Splitter Bar flag
  307.  
  308.     ' Change the cursor to splitter or back
  309.     ' Are we in the container's client area?
  310.     If X >= 0 And X <= .ScaleWidth Then
  311.         If Y >= .ScaleTop And _
  312.             Y <= .ScaleTop + .ScaleHeight Then
  313.             ' Are we in the splitter bar zone?
  314.             If fVertical Then
  315.                 If X < ctlSE.Left And X > xRight(ctlNW) Then fZone = True
  316.             Else
  317.                 If Y < ctlSE.Top And Y > yBottom(ctlNW) Then fZone = True
  318.             End If
  319.         End If
  320.     End If
  321.     
  322.     If fZone Then   ' We're over the splitter bar
  323.         If .MousePointer <> mpResize Then
  324.             mpOld = .MousePointer
  325.             .MousePointer = mpResize
  326.             Me.Capture = True
  327.         End If
  328.     ElseIf (.MousePointer = mpResize) And Not fDragging Then
  329.         .MousePointer = mpOld
  330.         Me.Capture = False
  331.     End If
  332.     
  333.     ' Move the splitter line if within range
  334.     If fDragging Then
  335.         If fVertical Then
  336.             xyNew = X
  337.             xyMinPos = dxyMin
  338.             xyMaxPos = .ScaleWidth - dxyMin
  339.         Else    ' We're moving the horizontal line
  340.             xyNew = Y
  341.             xyMinPos = .ScaleTop + dxyMin
  342.             xyMaxPos = .ScaleTop + .ScaleHeight - dxyMin
  343.         End If
  344.         If (xySplit <> xyNew) And _
  345.             (xyNew > xyMinPos) And (xyNew < xyMaxPos) Then
  346.             ' Erase the old line at xySplit and draw the new line
  347.             Draw xySplit, xyNew
  348.             xySplit = xyNew
  349.         End If
  350.     End If
  351. End With
  352. End Sub
  353.  
  354. Sub Splitter_MouseDown(Button As Integer, Shift As Integer, _
  355.                        X As Single, Y As Single)
  356. With objContainer
  357.     If Not fCreated Then ErrRaise eeSplitNotCreated
  358.     Dim fZone As Boolean
  359.     
  360.     If fVertical Then
  361.         fZone = X > xRight(ctlNW) And X < ctlSE.Left
  362.     Else
  363.         fZone = Y > yBottom(ctlNW) And Y < ctlSE.Top
  364.     End If
  365.     
  366.     ' If over splitter start a drag
  367.     If fZone Then
  368.         If Button = vbLeftButton Then
  369.             ' Save and restore state
  370.             fDragging = True
  371.             dsOld = .DrawStyle
  372.             dmOld = .DrawMode
  373.             arOld = .AutoRedraw
  374.             .AutoRedraw = False
  375.             ' Determine splitter line position
  376.             If fVertical Then
  377.                 xySplit = xRight(ctlNW) + (dxBorder / 3)
  378.             Else
  379.                 xySplit = yBottom(ctlNW) + (dyBorder / 3)
  380.             End If
  381.             ' Draw the splitter line
  382.             Draw xySplit
  383.         End If
  384.     Else
  385.         If .MousePointer = mpResize Then .MousePointer = mpOld
  386.     End If
  387. End With
  388. End Sub
  389.  
  390. Sub Splitter_MouseUp(Button As Integer, Shift As Integer, _
  391.                      X As Single, Y As Single)
  392. With objContainer
  393.     If Not fCreated Then ErrRaise eeSplitNotCreated
  394.     
  395.     If fDragging Then
  396.         ' Erase old line
  397.         Draw xySplit
  398.         fDragging = False
  399.  
  400.         ' Resize the panes if in range
  401.         If fVertical Then
  402.             If X > dxyMin And X < (.ScaleWidth - dxyMin) Then
  403.                 ctlNW.Width = X - ctlNW.Left - (dxySplit / 2)
  404.                 ctlSE.Left = xRight(ctlNW) + dxySplit
  405.                 ctlSE.Width = .ScaleWidth - ctlSE.Left - dxBorder
  406.             End If
  407.         Else
  408.             If Y > .ScaleTop + dxyMin And Y < (.ScaleTop + .ScaleHeight - dxyMin) Then
  409.                 ctlNW.Height = Y - ctlNW.Top - (dxySplit / 2)
  410.                 ctlSE.Top = yBottom(ctlNW) + dxySplit
  411.                 ctlSE.Height = .ScaleTop + .ScaleHeight - ctlSE.Top - dyBorder
  412.             End If
  413.         End If
  414.         .DrawStyle = dsOld
  415.         .DrawMode = dmOld
  416.         .AutoRedraw = arOld
  417.     End If
  418.     
  419.     ' Restore the pointer
  420.     If .MousePointer = mpResize Then
  421.          .MousePointer = mpOld
  422.          Me.Capture = False
  423.     End If
  424. End With
  425. End Sub
  426.  
  427. Sub Splitter_Resize()
  428.     If objContainer Is Nothing Then Exit Sub
  429.     If Not fCreated Then ErrRaise eeSplitNotCreated
  430.     On Error Resume Next
  431.     ' Only forms have WindowState
  432.     If objContainer.WindowState <> vbMinimized And fResize Then Resize
  433.     ' Must not be form
  434.     If Err And fResize Then Resize
  435. End Sub
  436.  
  437. Private Function xRight(obj As Object) As Single
  438.     xRight = obj.Left + obj.Width
  439. End Function
  440.  
  441. Private Function yBottom(obj As Object) As Single
  442.     yBottom = obj.Top + obj.Height
  443. End Function
  444.  
  445.  
  446.